A simple OOP library for Lua. It has inheritance, metamethods (operators), class variables and weak mixin support.
https://github.com/kikito/middleclass
前言
本文逐步的分析了middleclass模拟oop编程的过程。但是建议读者在看这篇文章之前对Lua的元表和元方法有一定的了解。
类定义
新建类定义
创建一个modulde,创建一个空白类定义对象,类定义对象需记录自己的name.
1 | ---middleclass--- |
测试:
1 | ---Animal--- |
添加__tostring函数
目前执行print(Animal)的时候,输出的是 table: 00B99B68 (类型: 指针地址)
可以让print的时候输出更友好的内容,比如 class Animal
解决方案
给新建类统一添加__tostring函数
print(Animal)时,会隐式调用tostring(Animal),接着会尝试调用getmetatable(Animal).__tostring(Animal)
1 | ---middleclass--- |
测试:
1 | ---Animal--- |
模拟Class关键字
目前创建一个类定义对象要调用如下写法Class.class(classname)
可以改为模拟oop Class关键字的风格Class(classname)
解决方案
给middleclass添加__call函数
把一个table当做函数调用时,会去尝试执行该table的metatable中的__call函数
1 | ---middleclass--- |
测试:
1 | ---Animal--- |
实例化
导入
A继承B的效果是什么?我们粗略的理解为,A不经显式声明就可以使用B的函数/变量。
middleclass使用了一个粗暴的手段:把父类中的所有属性都拷贝到子类中。middleclass没有把这种手段称为【继承】,而是借用了另外一个术语【导入(Include)】,这时父类被称为【Mixin】。
1 | ---middleclass--- |
实例化
1 | ---middleclass--- |
DefaultMixin
目前middleclass出现了两类函数:工具函数和class专用函数
class专用函数,比如,每个新创建的class都应拥有实例化的能力。class专用函数被打包到一个叫DefaultMixin表中,通过include方便的复制给每个新建class
1 | ---middleclass--- |
1 | ---Animal--- |
测试:
1 | ---test--- |
静态vs成员
lua本身没有静态与成员函数之分。
middleclass则定义的更为细致,更为贴近oop惯例。
class | instance | |
---|---|---|
静态函数 | 可调用 | 不可调用 |
成员函数 | 可调用(有什么意义?) | 可调用 |
- class可调用成员函数的意义? 必不可少的功能,不然怎么通过super来调用父类的成员函数。为实现上述目的,middleclass对某class上定义的函数进行了分组
- 也就是说,函数的声明都是在class级别进行的
- class.static : 只有class可以调用的函数
- class.__instanceDict : instance所调用的函数
static表
上面章节的class专用函数,比如实例化函数,middleclass将其当做静态函数处理;在别的语言中,实例化功能可能埋藏在语言实现的底层,以语言关键字的形式出现,而非静态函数。是的,关键就在于此:middleclass用静态函数来模拟new这个关键字。
middleclass用class.static来记录class中的静态函数,并将class.static设为class元表中__index的值中。class得以调用class.static中的函数
1 | ---middleclass--- |
__instanceDict表
middleclass把新定义的函数,MyClass.func()或者MyClass:func(),都通过__newindex元函数记录在class.__instanceDict表中,然后在该class创建实例时,把__instanceDict设为实例的metatable以及元表的__index的值
- 在middlecalss眼中,MyClass.func()或者MyClass:func()都是middleclass机制中的成员函数
- 在我们的应用层面,MyClass.func()像静态函数一样使用,MyClass:func()像成员函数一样使用
1 | ---middleclass--- |
一些细节
- 可以用 ClassA()的写法来new一个实例
- 实例对象的__tostring元函数
1 | ---middleclass--- |
1 | ---Animal--- |
测试:
1 | ---test--- |
继承
实现【继承】关键字
和new一样,middleclass用静态函数subclass来模拟实现extends关键字
1 | ---middleclass--- |
1 | ---Animal--- |
1 | ---Dog--- |
测试:
1 | ---test--- |
继承成员函数
middleclass的做法是把super中的所有成员函数在每个子类中都复制一份
- 创建subclass时,会调用_propagateInstanceMethod把当时super中所有成员函数复制到subclass中
- super声明新函数时,会把该新函数在super所有子类中的__instanceDict表中也复制一份
实际应用中大部分都是先完整的声明完super,再去创建其subclass。 否则的话,super声明新函数时,会先通过subclass中的\__declaredMethods判断subclass是否声明过该函数 若没有声明,将这个函数复制到subclass中的\__instanceDict表中,否则不做处理。
__declaredMethods表中记录的是通过声明创建的函数,__instanceDict表中记录了所有的函数:声明得到的,以及从所有父类那里复制得到的
基于上述做法,每个子类中的__instanceDict都包含了其所有父类的__instanceDict的集合。即子类拥有父类函数。
如果子类重新定义了一个同名函数,那么将覆盖掉父类函数。
子类可以通过super关键字来调用函数的父类版本
1 | ---middleclass--- |
1 | ---Animal--- |
1 | ---Dog--- |
测试:
1 | ---test--- |